package com.huawei.eidc.message;

import java.io.File;
import java.io.StringReader;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import com.huawei.eidc.slee.security.Base64;
import com.huawei.eidc.slee.security.DESTools;
import com.huawei.eidc.slee.security.MD5;

/**
 * <p>Title: EIDCMessageSample</p>
 *
 * <p>Description: Sample for Message Package and Unpack</p>
 *
 * <p>Copyright: Copyright (c) Huawei Technologies Co.,LTD 2006.All rights
 * reserved</p>
 *
 * <p>Company: Huawei Technologies Co.,LTD</p>
 *
 * @author lidafeng 54630
 * @version 1.0
 */
public class Sample {
        /**
         * 设置测试用密钥
         */
	public static final String TEST_KEY = "AD67EA2F3EE6E5AA";

    public Sample() {
    }

    /**
     * 测试主程序，调用消息打包和消息解包过程
     * @param args String[]
     */
    public static void main(String[] args) {
    	Sample sample = new Sample();
    	String path = "D:\\Developer\\Java\\WorkSpaceS\\Maven\\WorkSpace\\WebServices\\WebContent\\WEB-INF\\template\\";
    	File file = new File(path + "ECInfoQuery.xml");
    	SAXReader reader = new SAXReader();
    	Document document = null;
    	try {
    		document = reader.read(file);
    		System.out.println("模版文件：" + getXMLStr(document));
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("模版文件读取失败");
		}
    	
    	Element root = document.getRootElement();
    	Element head = root.element("HEAD");
    	head.element("CODE").addText("ECInfoQuery");
    	head.element("SID").addText("6786451");
    	String reqTime=new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date());
    	head.element("TIMESTAMP").addText(reqTime);
    	String body = sample.encodeBody(null, TEST_KEY);
    	root.element("BODY").addText(body);
    	
        System.out.println("消息打包...");

        String strTemp = getXMLStr(document);
        System.out.println(strTemp);
        if ( !strTemp.equals(null) ) {
        	System.out.println(strTemp);
            System.out.println("encode OK!");
        }
        else {
            System.out.println("encode ERROR!");
        }

//        一个简单的调用SOAP接口的例子，没有实际环境时无法测试
        try {
        	String address = "http://218.2.129.11:11070/ecagent/services/ECAgentService?wsdl";
            Service service = new Service();
            Call call = null;
            call = (Call) service.createCall();
            call.setTargetEndpointAddress(new URL(address));
            String smg = strTemp;
            String ret = (String) call.invoke("ecOrderQuery", new Object[] {strTemp});
            System.out.println("结果："+ret);
            System.out.println("响应："+sample.decrypt(getStrXML(ret).getRootElement().elementText("BODY"), TEST_KEY));
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }


//        System.out.println("消息打包完成.");
//        System.out.println();
//
//        System.out.println("消息解包...");
//        System.out.println();
//
//        if ( sample.decode(strTemp,TEST_KEY) ) {
//            System.out.println("decode OK!");
//        }
//        else {
//            System.out.println("decode ERROR!");
//        }
//        System.out.println("消息解包完成.");
    }

    /**
     * 消息打包过程
     * 将消息头和加密后的消息体放在一起组成整个消息串
     * @param desSecret String 密钥
     * @return String 加密后的消息串
     */
    public String encode(String desSecret)
    {
        org.dom4j.Document document = DocumentHelper.createDocument();
        Element msg = document.addElement("MSG");
        Element head = msg.addElement("HEAD");
        head.addElement("CODE").addText("103");
        head.addElement("SID").addText("00001");
        head.addElement("TIMESTAMP").addText(String.valueOf(System.currentTimeMillis()));
        head.addElement("SERVICEID").addText("01010101");
        msg.addElement("BODY").setText(encodeBody(desSecret));
        
        
        String Message = getXMLStr(document);
        System.out.println();
        System.out.println("打包好的消息：");
        System.out.println(Message);
        return Message;
    }

    /**
     * 对传入打包好的整个消息进行解码
     * 分别对头部和尾部进行处理
     * @param str String 打包好的消息字符串
     * @param desSecret String 密钥
     * @return boolean 返回解包成功失败
     */
    public boolean decode(String str, String desSecret)
    {
        if (str == null)
        {
            return false;
        }
        try{
            org.dom4j.Document document = DocumentHelper.parseText(str);
            System.out.println();
            System.out.println("消息头...");
            System.out.println();

            Element head = document.getRootElement().element("HEAD");
            System.out.println("CODE:");
            System.out.println(head.element("CODE").getTextTrim());
            System.out.println("SID:");
            System.out.println(head.element("SID").getTextTrim());
            System.out.println("TIMESTAMP:");
            System.out.println(Long.parseLong(head.element("TIMESTAMP").getTextTrim()));
            System.out.println("SERVICEID:");
            System.out.println(head.element("SERVICEID").getTextTrim());
            String strBody = document.getRootElement().element("BODY").getTextTrim();
            System.out.println("BODY:");
            System.out.println(strBody);
            System.out.println();
            System.out.println("消息头结束.");
            System.out.println();

            System.out.println("消息体...");
            System.out.println();
            if ( decodeBody(strBody,desSecret) ) {
                System.out.println("decodeBody OK!");
            }
            else {
                System.out.println("decodeBody ERROR!");
                return false;
            }
            System.out.println();
            System.out.println("消息体结束.");
            System.out.println();
        }
        catch(Exception ex)
        {
            return false;
        }
        return true;
    }

    /**
     * 组织消息体实例,以一段简单的XML为例
     * @param desSecret String 密钥
     * @return String 加密后的消息体字符串
     */
    public String encodeBody(String desSecret)
    {
        org.dom4j.Document document = DocumentHelper.createDocument();
        Element body = document.addElement("BODY");

        Element section = body.addElement("SECTION");
        section.addElement("NAME").addText("CORPNAME");
        section.addElement("VALUE").addText("华为技术有限公司");

        section = body.addElement("SECTION");
        section.addElement("NAME").addText("CORPTELE");
        section.addElement("VALUE").addText("28780808");
   
        System.out.println();
        System.out.println("组织好的消息体XML文档：");
        System.out.println(getXMLStr(document));
        
        return encrypt(getXMLStr(document), desSecret);
    }
    
    public String encodeBody(Element body, String desSecret)
    {
    	Document dNew = DocumentHelper.createDocument();
    	Element root = dNew.addElement("BODY");
    	Element ecid = root.addElement("ECID");
    	ecid.setText("1000");
        System.out.println("组织好的消息体XML文档：");
        System.out.println(getXMLStr(dNew));
        System.out.println("组织好的加密xml:"+encrypt(getXMLStr(dNew), desSecret));
        
        return encrypt(getXMLStr(dNew), desSecret);
    }
    

    /**
     * 对消息体进行解包识别输出实例
     * @param str String  消息体字符串
     * @param desSecret String 密钥
     * @return boolean 解包成功失败
     */
    public boolean decodeBody(String str, String desSecret)
    {
        String bodyStr = decrypt(str,desSecret);
        if (bodyStr == null) return false;
        try{
            org.dom4j.Document document = DocumentHelper.parseText(bodyStr);
            Element body = document.getRootElement();
            Iterator it = body.elementIterator("SECTION");
            while(it.hasNext())
            {
               Element es = (Element)it.next();
               System.out.println("NAME="+es.elementText("NAME"));
               System.out.println("VALUE="+es.elementText("VALUE"));
            }
        }
        catch(Exception ex)
        {
            return false;
        }
        return true;
    }

    /**
     * 传入XML格式的文档对象，返回转换为UTF8格式的字符串
     * @param document Document XML格式的文档对象
     * @return String 转换为UTF8格式的字符串
     */
    public static String getXMLStr(org.dom4j.Document document)
    {
        try
        {
            java.io.StringWriter sw = new java.io.StringWriter();
            OutputFormat format = OutputFormat.createCompactFormat();
            format.setEncoding("UTF8");
            org.dom4j.io.XMLWriter xmlWriter = new org.dom4j.io.XMLWriter(sw,format);
            xmlWriter.write(document);
            xmlWriter.close();
            return sw.getBuffer().toString();
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            return "";
        }
    }

    /**
      * 对字符串进行加密
      * 1. 数字签名保证信息完整性
      * 2. 3DES加密保证不可阅读性
      * 3. BASE64编码
      * Base64( 3DES( MD5( 消息体 ) + 消息体)
      * @param str String
      * @return String
      */
     public String encrypt(String str,String desSecret)
     {
         String body = null;

         try {
             DESTools des = DESTools.getInstance(desSecret);
             System.out.println("加密前的消息体:");
             System.out.println(str);
             String md5str = MD5.md5(str)+str;
             System.out.println("MD5后的字符串:");
             System.out.println(md5str);
             byte[] b = des.encrypt(md5str.getBytes("UTF8"));
             System.out.println("DES后的字节数组:");
             printBytes(b);
             body = Base64.encode(b);
             System.out.println("Base64后的字符串:");
             System.out.println(body);
         } catch (Exception ex) {

         }
         return body;
    }

    /**
     * 对字符串进行解密
     * Base64( 3DES( MD5( 消息体 ) + 消息体) 逆操作
     * @param str String
     * @return String
     * 解密时判断数据签名，如果不匹配则返回null
     */
    public String decrypt(String str,String desSecret)
    {
        String body = null;
        try {
            DESTools des = DESTools.getInstance(desSecret);
            System.out.println("对方传来的消息源串：");
            System.out.println(str);
            byte[] b = Base64.decode(str);
            System.out.println("Base64解码之后的结果：");
            printBytes(b);
            String md5body = new String(des.decrypt(b),"UTF8");
            System.out.println("3DES解密后的结果：");
            System.out.println(md5body);
            if(md5body.length()<32)
            {
                System.out.println("错误！消息体长度小于数字签名长度!");
                return null;
            }
            String md5Client = md5body.substring(0,32);
            System.out.println("对方传来的数字签名：");
            System.out.println(md5Client);
            body = md5body.substring(32);
            System.out.println("对方传来的消息体：");
            System.out.println(body);
            String md5Server = MD5.md5(body);
            System.out.println("我方对传来消息的数字签名：");
            System.out.println(md5Server);
            if(!md5Client.equals(md5Server))
            {
                System.out.println("错误！数字签名不匹配:");
                return null;
            }
        } catch (Exception ex) {

        }
        return body;
    }

    /**
     * 打印字节数组
     * @param b byte[]
     */
    public static void printBytes(byte[] b)
    {
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<b.length;i++)
        {
            sb.append("["+b[i]+"],");
            if(i%15==0)
            {
                sb.append("\n");
            }
        }
        System.out.println(sb);
    }
    
    /**
     * 字符串转xml对象
     * @param xml
     * @return
     */
    public static Document getStrXML(String xml){
    	Document doc = null;
    	try {
    		doc = DocumentHelper.parseText(xml);
		} catch (Exception e) {
			e.printStackTrace();
		}
    	
    	return doc;
    }
}
